/*
    Geocache: some utilities for managing and visualizing geocache information
    Copyright (C) 2008  Gary Jackson

    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */
/*
 * LinkedCircleList.java
 */

package com.thegbomb.common;

import java.lang.reflect.Array;
import java.util.Collection;
import java.util.Iterator;
import java.util.NoSuchElementException;

/**
 *
 * @author Gary Jackson
 */
public class LinkedCircleList<E> implements CircleList<E> {
    private class Elt {
        public Elt next = this, prev = this;
        public E content;
        
        public Elt(E content) {
            this.content = content;
        }
    }
    
    private Elt current = null;
    private int size = 0;
    
    /**
     * Creates a new instance of LinkedCircleList
     */
    public LinkedCircleList() {
        // Do nothing
    }
    
    public LinkedCircleList(Collection<E> c) {
        this.addAll(c);
    }
    
    public E getNext() {
        if (size == 0)
            throw new NoSuchElementException();
        else {
            current = current.next;
            return current.prev.content;
        }
    }
    
    public E getPrevious() {
        if (size == 0)
            throw new NoSuchElementException();
        else {
            current = current.prev;
            return current.content;
        }
    }
    
    private Elt delete(Elt e) {
        if (size-- == 1) {
            current = null;
            return null;
        } else {
            e.next.prev = e.prev;
            e.prev.next = e.next;
            return e.next;
        }
    }
    
    public E removeNext() {
        E ret = current.content;
        if (size == 0)
            throw new NoSuchElementException();
        else
            current = delete(current);
        return ret;
    }
    
    public E removePrevious() {
        E ret = current.prev.content;
        if (size == 0)
            throw new NoSuchElementException();
        else {
            delete(current.prev);
        }
        return ret;
    }
    
    private Elt insert(Elt e) {
        if (size == 0)
            current = e;
        else {
            e.next = current;
            e.prev = current.prev;
            current.prev.next = e;
            current.prev = e;
        }
        size ++;
        return e;
    }
    
    public void insertNext(E element) {
        current = insert(new Elt(element));
    }
    
    public void insertPrevious(E element) {
        insert(new Elt(element));
    }
    
    public int size() {
        return size;
    }
    
    public boolean isEmpty() {
        return size == 0;
    }
    
    public boolean contains(Object o) {
        for (E t: this) {
            if (t.equals(o))
                return true;
        }
        return false;
    }
    
    public Iterator<E> iterator() {
        return new Iterator<E>() {
            private Elt origCur = current;
            private Elt myCur = current;
            boolean more = true && (size != 0);
            
            public void remove() {
                throw new UnsupportedOperationException();
            }
            
            public E next() {
                if (size != 0) {
                    E content = myCur.content;
                    myCur = myCur.next;
                    if (myCur == origCur) more = false;
                    return content;
                } else
                    throw new NoSuchElementException();
            }
            
            public boolean hasNext() {
                return more;
            }
        };
    }
    
    public Object[] toArray() {
        Object ary[] = new Object[size];
        int i = 0;
        for (E t: this)
            ary[i++] = t;
        return ary;
    }
    
    public boolean add(E element) {
        this.insertPrevious(element);
        return true;
    }
    
    public boolean remove(Object o) {
        throw new UnsupportedOperationException();
    }
    
    public boolean containsAll(Collection<?> coll) {
        throw new UnsupportedOperationException();
    }
    
    public boolean addAll(Collection<? extends E> coll) {
        for (E e: coll)
            this.insertPrevious(e);
        return true;
    }
    
    public boolean removeAll(Collection<?> coll) {
        throw new UnsupportedOperationException();
    }
    
    public boolean retainAll(Collection<?> coll) {
        throw new UnsupportedOperationException();
    }
    
    public void clear() {
        current.next.prev = null;
        current.next = null;
        current = null;
        size = 0;
    }
    
    public <T> T[] toArray(T[] type) {
        E[] ary = (E[])(Array.newInstance(type.getClass().getComponentType(), size));
        int i = 0;
        for (E t: this)
            ary[i++] = t;
        return (T[])ary;
    }
}
